// Filename: lodNode.I
// Created by:  drose (06Mar02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////



////////////////////////////////////////////////////////////////////
//     Function: LODNode::Constructor
//       Access: Published
//  Description:
////////////////////////////////////////////////////////////////////
INLINE LODNode::
LODNode(const string &name) :
  PandaNode(name)
{
  set_cull_callback();
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Copy Constructor
//       Access: Protected
//  Description:
////////////////////////////////////////////////////////////////////
INLINE LODNode::
LODNode(const LODNode &copy) :
  PandaNode(copy),
  _cycler(copy._cycler)
{
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::add_switch
//       Access: Published
//  Description: Adds a switch range to the LODNode.  This implies
//               that the corresponding child node has been parented
//               to the node.
//
//               The sense of in vs. out distances is as if the object
//               were coming towards you from far away: it switches
//               "in" at the far distance, and switches "out" at the
//               close distance.  Thus, "in" should be larger than
//               "out".
////////////////////////////////////////////////////////////////////
INLINE void LODNode::
add_switch(PN_stdfloat in, PN_stdfloat out) {
  nassertv(in >= out);

  CDWriter cdata(_cycler);
  cdata->_switch_vector.push_back(Switch(in, out));
  cdata->check_limits();

  if (cdata->_num_shown != 0) {
    mark_internal_bounds_stale();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::set_switch
//       Access: Published
//  Description: Changes the switching range of a particular child of
//               the LODNode.  See add_switch().
////////////////////////////////////////////////////////////////////
INLINE bool LODNode::
set_switch(int index, PN_stdfloat in, PN_stdfloat out) {
  nassertr(in >= out, false);

  CDWriter cdata(_cycler);
  nassertr(index >= 0 && index < (int)cdata->_switch_vector.size(), false);
  cdata->_switch_vector[index].set_range(in, out);
  cdata->check_limits();

  if (cdata->_num_shown != 0) {
    mark_internal_bounds_stale();
  }

  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::clear_switches
//       Access: Published
//  Description: Removes the set of switching ranges for the LODNode,
//               presumably in conjunction with removing all of its
//               children.  See add_switch().
////////////////////////////////////////////////////////////////////
INLINE void LODNode::
clear_switches() {
  CDWriter cdata(_cycler);
  cdata->_switch_vector.clear();
  cdata->_lowest = 0;
  cdata->_highest = 0;

  if (cdata->_num_shown != 0) {
    mark_internal_bounds_stale();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::get_num_switches
//       Access: Published
//  Description: Returns the number of switch ranges added to the
//               LODNode.  This should correspond to the number of
//               children of the node in order for the LODNode to
//               function correctly.
////////////////////////////////////////////////////////////////////
INLINE int LODNode::
get_num_switches() const {
  CDReader cdata(_cycler);
  return cdata->_switch_vector.size();
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::get_lod_scale
//       Access: Published
//  Description: Returns the multiplier for lod distances
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat LODNode::
get_lod_scale() const {
  CDReader cdata(_cycler);
  return cdata->_lod_scale;
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::set_lod_scale
//       Access: Published
//  Description: Sets the multiplier for lod distances. A higher 
//               value means you'll see farther switchs than normal
////////////////////////////////////////////////////////////////////
INLINE void LODNode::
set_lod_scale(PN_stdfloat value) {
  CDWriter cdata(_cycler);
  cdata->_lod_scale = value;
}


////////////////////////////////////////////////////////////////////
//     Function: LODNode::get_in
//       Access: Published
//  Description: Returns the "in" distance of the indicated switch
//               range.  This should be larger than the "out" distance
//               of the same range.
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat LODNode::
get_in(int index) const {
  CDReader cdata(_cycler);
  nassertr(index >= 0 && index < (int)cdata->_switch_vector.size(), 0.0);
  return cdata->_switch_vector[index].get_in();
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::get_out
//       Access: Published
//  Description: Returns the "out" distance of the indicated switch
//               range.  This should be smaller than the "in" distance
//               of the same range.
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat LODNode::
get_out(int index) const {
  CDReader cdata(_cycler);
  nassertr(index >= 0 && index < (int)cdata->_switch_vector.size(), 0.0);
  return cdata->_switch_vector[index].get_out();
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::get_lowest_switch
//       Access: Published
//  Description: Returns the index number of the child with the lowest
//               level of detail; that is, the one that is designed to
//               be seen from the farthest away.  This is usually the
//               first child, but it is not necessarily so.
////////////////////////////////////////////////////////////////////
INLINE int LODNode::
get_lowest_switch() const {
  CDReader cdata(_cycler);
  return cdata->_lowest;
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::get_highest_switch
//       Access: Published
//  Description: Returns the index number of the child with the highest
//               level of detail; that is, the one that is designed to
//               be seen from the closest to the camera.  This is
//               usually the last child, but it is not necessarily so.
////////////////////////////////////////////////////////////////////
INLINE int LODNode::
get_highest_switch() const {
  CDReader cdata(_cycler);
  return cdata->_highest;
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::force_switch
//       Access: Published
//  Description: Forces the LODNode to show the indicated level
//               instead of the level that would normally be shown
//               based on the distance from the camera.
////////////////////////////////////////////////////////////////////
INLINE void LODNode::
force_switch(int index) {
  CDWriter cdata(_cycler);
  cdata->_force_switch = index;
  cdata->_got_force_switch = true;
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::clear_force_switch
//       Access: Published
//  Description: Undoes the effect of a previous call to
//               force_switch() and releases the LODNode to once again
//               display the normal level.
////////////////////////////////////////////////////////////////////
INLINE void LODNode::
clear_force_switch() {
  CDWriter cdata(_cycler);
  cdata->_got_force_switch = false;
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::set_center
//       Access: Published
//  Description: Specifies the center of the LOD.  This is the point
//               that is compared to the camera (in camera space) to
//               determine the particular LOD that should be chosen.
////////////////////////////////////////////////////////////////////
INLINE void LODNode::
set_center(const LPoint3 &center) {
  CDWriter cdata(_cycler);
  cdata->_center = center;

  if (cdata->_num_shown != 0) {
    mark_internal_bounds_stale();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::get_center
//       Access: Published
//  Description: Returns the center of the LOD.  This is the point
//               that is compared to the camera (in camera space) to
//               determine the particular LOD that should be chosen.
////////////////////////////////////////////////////////////////////
INLINE const LPoint3 &LODNode::
get_center() const {
  CDReader cdata(_cycler);
  return cdata->_center;
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::is_any_shown
//       Access: Published
//  Description: Returns true if any switch has been shown with
//               show_switch(), indicating the LODNode is in debug
//               show mode; or false if it is in the normal mode.
////////////////////////////////////////////////////////////////////
INLINE bool LODNode::
is_any_shown() const {
  CDReader cdata(_cycler);
  return (cdata->_num_shown != 0);
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::consider_verify_lods
//       Access: Protected
//  Description: To be called internally when the node is rendered,
//               this will raise an assertion if verify-lods is
//               configured true, and verify_child_bounds() returns
//               false.
////////////////////////////////////////////////////////////////////
INLINE void LODNode::
consider_verify_lods(CullTraverser *trav, CullTraverserData &data) {
#ifndef NDEBUG
  if (verify_lods) {
    do_auto_verify_lods(trav, data);
  }
#endif  // NDEBUG
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::CData::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
INLINE LODNode::CData::
CData() :
  _center(0.0f, 0.0f, 0.0f),
  _lowest(0),
  _highest(0),
  _got_force_switch(false),
  _force_switch(0),
  _num_shown(0),
  _lod_scale(1)
{
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::CData::Copy Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
INLINE LODNode::CData::
CData(const LODNode::CData &copy) :
  _center(copy._center),
  _switch_vector(copy._switch_vector),
  _lowest(copy._lowest),
  _highest(copy._highest),
  _bounds_seq(UpdateSeq::old()),
  _got_force_switch(copy._got_force_switch),
  _force_switch(copy._force_switch),
  _num_shown(copy._num_shown),
  _lod_scale(copy._lod_scale)
{
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Switch::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
INLINE LODNode::Switch::
Switch(PN_stdfloat in, PN_stdfloat out) : 
  _shown(false),
  _bounds_seq(UpdateSeq::old()),
  _verify_ok(false)
{
  set_range(in, out);
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Switch::get_in
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat LODNode::Switch::
get_in() const {
  return _in;
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Switch::get_out
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
INLINE PN_stdfloat LODNode::Switch::
get_out() const {
  return _out;
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Switch::set_range
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
INLINE void LODNode::Switch::
set_range(PN_stdfloat in, PN_stdfloat out) {
  _in = in;
  _out = out;
  clear_ring_viz();
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Switch::in_range
//       Access: Public
//  Description: Returns true if the indicated distance is within the
//               range for the LOD.
////////////////////////////////////////////////////////////////////
INLINE bool LODNode::Switch::
in_range(PN_stdfloat dist) const {
  return (dist >= _out && dist < _in);
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Switch::in_range_2
//       Access: Public
//  Description: Returns true if the indicated distance squared is
//               within the range for the LOD.  (The distance value is
//               understood to be the square of the distance from the
//               camera to the object.)
////////////////////////////////////////////////////////////////////
INLINE bool LODNode::Switch::
in_range_2(PN_stdfloat dist2) const {
  return (dist2 >= _out * _out && dist2 < _in * _in);
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Switch::rescale
//       Access: Public
//  Description: Scales the switching distances by the indicated factor.
////////////////////////////////////////////////////////////////////
INLINE void LODNode::Switch::
rescale(PN_stdfloat factor) {
  _in *= factor;
  _out *= factor;
  clear_ring_viz();
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Switch::is_shown
//       Access: Public
//  Description: Returns true if show() has been called.
////////////////////////////////////////////////////////////////////
INLINE bool LODNode::Switch::
is_shown() const {
  return _shown;
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Switch::show
//       Access: Public
//  Description: Shows this ring in debug mode using the indicated
//               color.
////////////////////////////////////////////////////////////////////
INLINE void LODNode::Switch::
show(const LColor &color) {
  _shown = true;
  _show_color = color;
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Switch::hide
//       Access: Public
//  Description: Undoes a previous call to show().
////////////////////////////////////////////////////////////////////
INLINE void LODNode::Switch::
hide() {
  _shown = false;
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Switch::get_ring_viz
//       Access: Public
//  Description: Returns a PandaNode suitable for rendering the ring
//               associated with this switch.
////////////////////////////////////////////////////////////////////
INLINE PandaNode *LODNode::Switch::
get_ring_viz() const {
  if (_ring_viz.is_null()) {
    ((Switch *)this)->compute_ring_viz();
  }

  return _ring_viz;
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Switch::get_spindle_viz
//       Access: Public
//  Description: Returns a PandaNode suitable for rendering the center
//               spindle of the LODNode, in the color of this switch.
////////////////////////////////////////////////////////////////////
INLINE PandaNode *LODNode::Switch::
get_spindle_viz() const {
  if (_spindle_viz.is_null()) {
    ((Switch *)this)->compute_spindle_viz();
  }

  return _spindle_viz;
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Switch::get_viz_model_state
//       Access: Public
//  Description: Returns a RenderState suitable for drawing the
//               visible children of this switch level when the
//               show_switch() debugging mode is enabled.
////////////////////////////////////////////////////////////////////
INLINE const RenderState *LODNode::Switch::
get_viz_model_state() const {
  if (_viz_model_state.is_null()) {
    ((Switch *)this)->compute_viz_model_state();
  }

  return _viz_model_state;
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Switch::write_datagram
//       Access: Public
//  Description: Writes the contents of the Switch out to the
//               datagram, presumably in preparation to writing to a
//               Bam file.
////////////////////////////////////////////////////////////////////
INLINE void LODNode::Switch::
write_datagram(Datagram &destination) const {
  destination.add_stdfloat(_in);
  destination.add_stdfloat(_out);
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Switch::read_datagram
//       Access: Public
//  Description: Reads the contents of the Switch from the datagram,
//               presumably in response to reading a Bam file.
////////////////////////////////////////////////////////////////////
INLINE void LODNode::Switch::
read_datagram(DatagramIterator &source) {
  _in = source.get_stdfloat();
  _out = source.get_stdfloat();
}

////////////////////////////////////////////////////////////////////
//     Function: LODNode::Switch::clear_ring_viz
//       Access: Private
//  Description: Resets the internal cache values for the ring and
//               spindle viz, and related pointers, for the
//               set_switch() debugging mode.
////////////////////////////////////////////////////////////////////
INLINE void LODNode::Switch::
clear_ring_viz() {
  _ring_viz.clear();
  _spindle_viz.clear();
  _viz_model_state.clear();
  _bounds_seq = UpdateSeq::old();
}
